﻿using System;
using System.Configuration;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Nana.Web
{
  static class ConfigurationExtensions
  {
    static readonly Type objectType = typeof(object);
    static readonly Type stringType = typeof(string);
    static readonly Type cfgPropAttrType = typeof(ConfigurationPropertyAttribute);

    static readonly PropertyInfo propertyItem;

    static ConfigurationExtensions()
    {
      propertyItem = typeof(ConfigurationElement).GetProperty("Item",
        BindingFlags.Instance | BindingFlags.NonPublic, null, objectType,
        new[] { stringType }, new ParameterModifier[] { });
    }

    static void ValidateAsExpressionTarget(this object target, Expression expr)
    {
      switch (expr.NodeType)
      {
        case ExpressionType.Parameter:
          break;
        case ExpressionType.Constant:
          if ((expr as ConstantExpression).Value != target)
            throw new InvalidOperationException();
          break;
        default:
          throw new InvalidOperationException();
      }
    }

    public static TValue GetConfig<TElement, TValue>(this TElement element,
      Expression<Func<TElement, TValue>> lambda)
      where TElement : ConfigurationElement
    {
      var access = lambda.Body as MemberExpression;
      element.ValidateAsExpressionTarget(access.Expression);

      var prop = access.Member as PropertyInfo;
      var name = (prop.GetCustomAttributes(cfgPropAttrType, false)
        .Single() as ConfigurationPropertyAttribute).Name;

      return (TValue)propertyItem.GetValue(element, new[] { name });
    }
    public static void SetConfig<TElement, TValue>(this TElement element,
      Expression<Func<TElement, TValue>> lambda,
      TValue value)
      where TElement : ConfigurationElement
    {
      var access = lambda.Body as MemberExpression;
      element.ValidateAsExpressionTarget(access.Expression);

      var prop = access.Member as PropertyInfo;
      var name = (prop.GetCustomAttributes(cfgPropAttrType, false)
        .Single() as ConfigurationPropertyAttribute).Name;

      propertyItem.SetValue(element, value, new[] { name });
    }

  }
}
